﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using ScriptingExtension;
using Auxiliaries;

namespace ConsoleScriptingApp
{
    class Program
    {
        private static Semaphore instanceCreated;

        static string addr = "192.168.0.5";
        static int port = 1234;
        static string omxCopm = "";
        static string scrDir = "";

        class ThreadParameters
        {
            public OMXScriptCtrl omxCtrl;
            public string command;
            public bool IsFile;
        }

        static ThreadParameters inptP = new ThreadParameters();

        static AutoResetEvent execScrW = new AutoResetEvent(false);

        static int parseInputParams(string[] args)
        {
            if (args.Length > 0)
            {
                FileInfo loadPjt = new FileInfo(args[0]);
                if (loadPjt.Exists)
                {
                    omxCopm = loadPjt.FullName;
                }
                else
                {
                    Console.WriteLine("File " + loadPjt.FullName + " doesn't exist!");
                    return -1;
                }

                for (int i = 1; i < args.Length; i++)
                {
                    switch (args[i])
                    {
                        case "-tcp":
                            if (++i < args.Length)
                            {
                                addr = args[i];
                            }
                            else
                            {
                                Console.WriteLine("Incorrect usage: -tcp <xxx.xxx.xxx.xxx> expects a valid TCP/IP address");
                                i = args.Length;
                            }
                            break;
                        case "-port":
                            if (!(++i < args.Length && int.TryParse(args[i], out port)))
                            {
                                Console.WriteLine("Incorrect usage: -port <xxxx> expects a valid TCP/IP port");
                                i = args.Length;
                            }
                            break;
                        case "-script_dir":
                            if (++i < args.Length)
                            {
                                DirectoryInfo dirInf = new DirectoryInfo(args[i]);
                                if (dirInf.Exists)
                                {
                                    scrDir = dirInf.FullName;
                                }
                                else
                                {
                                    Console.WriteLine("Direcotry " + dirInf.FullName + " doesn't exist!");
                                    i = args.Length;
                                }
                            }
                            else
                                Console.WriteLine("Incorrect usage: -script_dir <fdirectory_path> expects a valid directory path");
                            break;
                        default:
                            Console.WriteLine("Unknown command. Usage tip:\n" +
                                                "-script_dir <fdirectory_path> sets the scripts directory for relative script calls.\n" +
                                                "-tcp <xxx.xxx.xxx.xxx> sets TCP/IP host address" +
                                                "-port <xxxx> sets TCP/IP host port");
                            i = args.Length;
                            break;
                    }
                }

                return 0;
            }
            else
            {
                Console.WriteLine("Unknown command. Usage tip: <file_path> to OMX component [-script_dir | -tcp | -port]");
                return -1;
            }
        }

        /// <summary>
        /// Reads a complete word from a string - word defined by two spaces
        /// </summary>
        /// <param name="index_pos">The position within the string where to start the reading</param>
        /// <param name="line">The string which is to be read</param>
        /// <returns>The word extratced from the string</returns>
        static public string ReadWord(ref int index_pos, string line)
        {
            //The parsed word result
            String word = "";

            if (index_pos > -1 && line.Length > 0)
            {
                while (index_pos < line.Length && (Calculator.CharIsWordSeparator(line[index_pos])))
                    index_pos++;

                while (index_pos < line.Length && !Calculator.CharIsWordSeparator(line[index_pos]))
                {
                    word += line[index_pos++];
                }
            }
            return word;
        }

        static void Main(string[] args)
        {
            try
            {
                instanceCreated = Semaphore.OpenExisting("OMXConfigPCToolInstanceCount");
                Console.WriteLine("Multiple instances of this application are not allowed!");
                return;
            }
            catch
            {
                instanceCreated = new Semaphore(0, 1, "OMXConfigPCToolInstanceCount");
            }

            OMXScriptCtrl omxControl = new OMXScriptCtrl();
            omxControl.OnStatusUpdate += new OMXScriptCtrl.StatusUpdate(omxControl_OnStatusUpdate);
            omxControl.GetTCPComm(out addr, out port);
            scrDir = omxControl.GetScriptsDirectory();

            if (parseInputParams(args) != 0)
            {
                return;
            }

            PrintF printFunc = new PrintF(omxControl_PrintF);

            Console.WriteLine("Loading component " + omxCopm);
            omxControl.LoadOMXComponent(omxCopm);
            Console.WriteLine("Done");
            Console.WriteLine("Configuring scripting module: " + scrDir);
            omxControl.SetScriptsDirectory(scrDir);
            Console.WriteLine("Type h or help for help\n");
            
            string cmd = "";
            string rP = "";
            string op = "";
            inptP.omxCtrl = omxControl;
            Thread scriptExec = new Thread(new ThreadStart(ExecuteScript));
            scriptExec.IsBackground = true;
            scriptExec.Start();
            
            do
            {
                int indx = 0;
                int snipLen = 0;
                cmd = Console.ReadLine();
                op = ReadWord(ref indx, cmd);
                

                switch (op)
                {
                    case "q":
                        Console.WriteLine("Quit? y/n");
                        if (Console.ReadLine() == "y")
                            break;
                        else
                            continue;
                    case "h":
                    case "help":
                        Console.WriteLine("**************************************************************");
                        Console.WriteLine("Input script comamd or:\n" +
                                            "\t'f <file path>'-execute a script file;\n"+
                                            "\t'w'-pause script;\n" +
                                            "\t'r'-resume script;\n" +
                                            "\t'g <line number>'-set next script statement on line number(if valid line)\n" + 
                                            "\t'e [lines]'-print current script snippet [prints optional number of lines centered on current line];\n" +
                                            "\t'c'-connect;\n" +
                                            "\t'd'-disconnect;\n" +
                                            "\t'a <tcp> <port>'-set tcp address;\n" +
                                            "\t's <directory path>'-set script directory\n" +
                                            "\t'x'-terminate current script execution\n" +
                                            "\t'q'-quit;");
                        Console.WriteLine("**************************************************************");
                        break;
                    case "c":
                        Console.WriteLine("Attempting connection to mms_il_module on " + addr + " port " + port.ToString());
                        omxControl.OpenTCPComm(addr, port);
                        Console.WriteLine("Starting scripting module");
                        omxControl.StartScriptingModule();
                        omxControl.RegisterPrintFunction(printFunc);
                        break;
                    case "d":
                        Console.WriteLine("Stopping scripting module");
                        omxControl.StopScriptingModule();
                        Console.WriteLine("Disconnecting from mms_il_module");
                        omxControl.CloseTCPComm();
                        break;
                    case "a":
                        addr = ReadWord(ref indx, cmd);
                        rP = ReadWord(ref indx, cmd);
                        if (!int.TryParse(rP, out port))
                            Console.WriteLine("Invalid port!");
                        Console.WriteLine("Setting TCP/IP and port");
                        break;
                    case "s":
                        scrDir = ReadWord(ref indx, cmd);
                        Console.WriteLine("Setting script directory");
                        omxControl.SetScriptsDirectory(scrDir);
                        break;
                    case "f":
                        rP = "";
                        while (indx < cmd.Length && (Calculator.CharIsWordSeparator(cmd[indx])))
                                indx++;
                        if (indx < cmd.Length)
                            rP = cmd.Substring(indx);

                        if (rP != "")
                        {
                            inptP.command = rP;
                            inptP.IsFile = true;
                            execScrW.Set();
                        }
                        else
                        {
                            Console.WriteLine("Provide filapath after 'f' argument!");
                        }
                        break;
                    case "w":
                        Console.WriteLine("Script stepping...");
                        omxControl.StepScript();
                        break;
                    case "r":
                        Console.WriteLine("Script resumed...");
                        omxControl.ResumeScript();
                        break;
                    case "e":
                        Console.WriteLine("\n\n***Snippet***\n\n");
                        rP = ReadWord(ref indx, cmd);
                        if(int.TryParse(rP, out snipLen))
                            Console.WriteLine(omxControl.ScriptSnippet(snipLen));
                        else
                            Console.WriteLine(omxControl.ScriptSnippet(1));
                        Console.WriteLine("\n\n***End***\n\n");
                        break;
                    case "x":
                        Console.WriteLine("Terminating current script");
                        omxControl.TerminateScriptExecution();
                        break;
                    case "g":
                        rP = ReadWord(ref indx, cmd);
                        if (int.TryParse(rP, out snipLen))
                            omxControl.SetExecutionLineNum(snipLen);
                        else
                            Console.WriteLine("Cant perform go to line with invalid integer!");
                        break;
                    case "gets":
                        rP = ReadWord(ref indx, cmd);
                        byte[] tests = omxControl.GetStructureBytestream(rP);
                        for (int i = 0; i < tests.Length; i++)
                        {
                            Console.WriteLine(string.Format(" 0x{0:X8} ", tests[i]));
                        }
                        break;
                    case "getf":
                        rP = ReadWord(ref indx, cmd);
                        string rP2 = ReadWord(ref indx, cmd);
                        byte[] testf = omxControl.GetStructFieldBytestream(rP, rP2);
                        for (int i = 0; i < testf.Length; i++)
                        {
                            Console.WriteLine(string.Format(" 0x{0:X8} ", testf[i]));
                        }
                        break;
                    default:
                        inptP.command = cmd;
                        inptP.IsFile = false;
                        execScrW.Set();
                        break;
                }
            } while (cmd != "q");
        }

        static void ExecuteScript()
        {
            while (true)
            {
                execScrW.WaitOne();

                string eMsg = "";
                uint eStat = 0;
                if (inptP.IsFile)
                {
                    Console.WriteLine("Executing " + inptP.command + "...");
                    eStat = inptP.omxCtrl.ExecuteScriptFile(inptP.command, out eMsg);
                    Console.WriteLine(" done with status: " + eMsg + "\nError: " + eStat.ToString());
                }
                else
                {
                    Console.WriteLine("Executing...");
                    eStat = inptP.omxCtrl.ExecuteScriptCommand(inptP.command, out eMsg);
                    Console.WriteLine(" done with status: " + eMsg + "\nError: " + eStat.ToString());
                }
            }
        }

        static void omxControl_OnStatusUpdate(OMXScriptCtrl.StatusUpdates stat, int custSt)
        {
            switch (stat)
            {
                case OMXScriptCtrl.StatusUpdates.ServerConnected:
                    Console.WriteLine("Server connected");
                    break;
                case OMXScriptCtrl.StatusUpdates.ServerDisconnected:
                    Console.WriteLine("Server disconnected");
                    break;
                case OMXScriptCtrl.StatusUpdates.ServerError:
                    Console.WriteLine("Server error");
                    break;
            }
        }

        static void omxControl_PrintF(string text)
        {
            Console.WriteLine(text);
        }
    }
}
